feat: add RAGAS adapter for third-party eval metrics - #618
Conversation
jariy17
left a comment
There was a problem hiding this comment.
YOu have merge conflicts and small nit
| assert result.value is not None | ||
|
|
||
|
|
||
| def _make_ragas_evaluator_input(user_prompt, agent_response): |
There was a problem hiding this comment.
Good catch — switched to _make_agent_evaluator_input in the latest push. The separate helper was a leftover from earlier development when the shared one used a span format the mappers couldn't parse, but that's been fixed on main since. All ragas integ tests should now use the shared helper.
Remove the ragas-specific _make_ragas_evaluator_input helper — the shared _make_agent_evaluator_input now uses the correct CloudWatch span format that the span mappers support.
309f760 to
8823edf
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #618 +/- ##
=======================================
Coverage ? 88.94%
=======================================
Files ? 122
Lines ? 10414
Branches ? 1609
=======================================
Hits ? 9263
Misses ? 760
Partials ? 391
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| outcome = self._score_legacy(fields) | ||
| elif hasattr(self.metric, "multi_turn_score"): | ||
| outcome = self._score_multi_turn(fields, span_result) | ||
| elif hasattr(self.metric, "ascore") and hasattr(self.metric, "score"): |
There was a problem hiding this comment.
[P1] I think collections multi-turn metrics bypass the field conversion here. _score_multi_turn() builds the RAGAS messages and reference_tool_calls, but collections metrics go directly to _score_collections() with a string user_input and no reference tools. Real RAGAS 0.4.3 collections.ToolCallAccuracy returned MISSING_REQUIRED_FIELD on the live Strands session despite expectedTrajectory.toolNames; the same metric scored 1.0 when given the converted fields. Could we prepare those fields for collections signatures too and test the real collections class? The current test uses the deprecated legacy class with a custom mapper, so it does not exercise this path.
There was a problem hiding this comment.
Fixed — _score_collections() now inspects each ascore() parameter and substitutes the converted messages / tool calls when the declared type is a sequence, sharing the conversion with the multi-turn path. Single-turn signatures still get flat strings. Also picks up ToolCallF1, AgentGoalAccuracy*, and TopicAdherence.
Substitution is limited to a {user_input, reference_tool_calls} allowlist rather than "any sequence-typed param" — retrieved_contexts: List[str] is sequence-typed in ~15 collections metrics and would have been clobbered.
One follow-on: with the conversion in place it scored 0.0, not 1.0. toolNames carries no arguments, and ToolCallAccuracy multiplies sequence alignment by argument accuracy, so any agent passing arguments scored 0. The adapter now adopts predicted arguments for argument-less same-named references, in call order. This is an interpretation of toolNames rather than something ragas specifies — it hides no signal since there was never argument ground truth, but happy to make it opt-in.
Tests use the shared _make_agent_evaluator_input (now with tool_calls= / reference_tool_names=) and no custom mapper: real collections and legacy ToolCallAccuracy score 1.0 on the correct tool, 0.0 on the wrong one, plus unit coverage for the substitution and alignment rules.
Note: collections TopicAdherence still needs a custom_mapper for reference_topics — no event field for it, since it's evaluator config rather than per-trace ground truth.
Also included a no-behavior-change simplification pass (unified legacy sample scoring, deduplicated error mapping, stdlib imports at module scope), verified across 17 scenarios against the pre-refactor adapter. Branch is merged up to latest main.
14cb808 to
8823edf
Compare
# Conflicts: # uv.lock
Collections metrics such as collections.ToolCallAccuracy declare user_input as a message list and require reference_tool_calls, but they route to _score_collections(), which passed the flat single-turn fields. The real metric returned MISSING_REQUIRED_FIELD on a live session even though expectedTrajectory.toolNames was present. - Share conversation-field construction between the legacy multi-turn and collections paths, selecting it per parameter by declared type so single-turn signatures still receive flat strings. Substitution is limited to an explicit allowlist because retrieved_contexts is sequence-typed in many collections metrics. - Adopt predicted arguments for argument-less reference tool calls, consumed in call order per tool name: toolNames expresses tool selection and sequence, so argument-aware metrics no longer count every argument as a mismatch. - Detect bare 'list' annotations, which have no typing origin. - Cover real collections and legacy ToolCallAccuracy through default span mapping, plus the argument-alignment rules. Also simplifies the adapter with no behavior change: one shared helper for legacy sample scoring, one place mapping scoring failures to error outputs, stdlib imports at module scope, and a return-type alias. Verified against the pre-refactor adapter across 17 scenarios.
aidandaly24
left a comment
There was a problem hiding this comment.
Thanks for making the fixes. This looks good to me now. I also tested and itw orks as expected.
Summary
Adds
RAGASAdapterto the third-party evaluation adapters, alongside the existingDeepEvalAdapterandAutoEvalsAdapter. Wraps any RAGAS metric as an AgentCore code-based Lambda evaluator with the sameBaseAdapterpattern.Design
Scores metrics through RAGAS's per-sample APIs (
metric.single_turn_score()/metric.multi_turn_score()for legacy metrics,metric.score(**kwargs)forragas.metrics.collectionsmetrics) rather than the batchragas.evaluate()pipeline. This means the adapter itself adds no dependency ondatasets/pyarrow/pandas, which makes it compatible with slim ragas builds for size-constrained Lambda deployments.Three-way dispatch:
SingleTurnSample+single_turn_score()MultiTurnSample+multi_turn_score()ragas.metrics.collections,@discrete_metric,@numeric_metric):metric.score(**fields)with signature-based field filteringFeatures
metric.required_columnsbefore scoring — returnsMISSING_REQUIRED_FIELDwith actionable guidance instead of letting ragas return silent 0.0 scores\n\nReference Answer:\nand\n\nContext:\nseparators) since ADOT trace formats have no dedicated fields for thesethreshold=Nonemetrics (e.g. SemanticSimilarity) and provides an adapter-levelthreshold=override for collections metrics which carry noneMetricResult.reasonas the explanationImportErrorat score time returnsMISSING_DEPENDENCY(not a generic crash)RAGASAdapteris the canonical class;RagasAdapteralias providedUsage
Known Limitations
reference_contexts(ground-truth contexts for reference-comparison metrics) is not defaulted from retrieved contexts — supply it viacustom_mapperwhen a genuine ground-truth source existsreference_tool_callsbuilt fromexpected_trajectory.toolNamescarry names without arguments; arg-accurate comparison needs acustom_mapperwith fullToolCallobjectsreference_topics(TopicAdherenceScore) has no span source — requirescustom_mapperdatasetswhen the ragas package is imported; the adapter is compatible with trimmed builds but does not solve that import itselfDependency Notes
ragas>=0.4.3,<1.0.0— the tested version rangelangchain-community>=0.3.0,<0.4.2— langchain-community 0.4.2 removedchat_models.vertexai, which ragas <1.0 imports unconditionallyragasto the evaluation integ testextra-depsin CIuv.lockregenerated for the new extra; incidental bumps tolangchain-communityresolution onlyTesting
@discrete_metric, ToolCallAccuracy match/mismatch, adapter-imports-without-datasets)uv lock --checkpasses